﻿@page "/Account/Manage/Passkeys"

@using BlazorWebCSharp._1.Data
@using Microsoft.AspNetCore.Identity
@using System.ComponentModel.DataAnnotations
@using System.Buffers.Text

@inject UserManager<ApplicationUser> UserManager
@inject SignInManager<ApplicationUser> SignInManager
@inject IdentityRedirectManager RedirectManager

<PageTitle>Manage your passkeys</PageTitle>

<h3>Manage your passkeys</h3>


<FluentGrid>
    <FluentGridItem xs="12" sm="6">
        <StatusMessage />

        @if (currentPasskeys is { Count: > 0 })
        {
            <table style="margin-top: 10px;">
                <thead>
                    <tr>
                        <th width="60%">Name</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var passkey in currentPasskeys)
                    {
                        <tr style="height: 44px;">
                            <td>@(passkey.Name ?? "Unnamed passkey")</td>
                            <td style="display: flex;align-items: center;height: 44px;justify-content: flex-end; gap: 5px;">
                                @{
                                    var credentialId = Base64Url.EncodeToString(passkey.CredentialId);
                                }
                                <form @formname="@($"update-passkey-rename-{credentialId}")" @onsubmit="UpdatePasskey" method="post">
                                    <AntiforgeryToken />
                                    <div>
                                        <input type="hidden" name="CredentialId" value="@credentialId" />
                                        <FluentButton Type="ButtonType.Submit" Name="Action" Value="rename" Appearance="Appearance.Accent" Title="Rename this passkey">Rename</FluentButton>
                                    </div>
                                </form>
                                <form @formname="@($"update-passkey-delete-{credentialId}")" @onsubmit="UpdatePasskey" method="post">
                                    <AntiforgeryToken />
                                    <div>
                                        <input type="hidden" name="CredentialId" value="@credentialId" />
                                        <FluentButton Type="ButtonType.Submit" Name="Action" Value="delete" BackgroundColor="var(--error)" Color="var(--foreground-on-accent-rest)" Title="Remove this passkey from your account">Delete</FluentButton>
                                    </div>
                                </form>
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        }
        else
        {
            <p>No passkeys are registered.</p>
        }
    <form @formname="add-passkey" @onsubmit="AddPasskey" method="post">
                <AntiforgeryToken />
    @if (currentPasskeys is { Count: >= MaxPasskeyCount })
    {
        <p class="text-danger">You have reached the maximum number of allowed passkeys. Please delete one before adding a new one.</p>
    }
    else
    {
        <PasskeySubmit Operation="PasskeyOperation.Create" Name="Input" class="btn btn-primary">Add a new passkey</PasskeySubmit>
    }

        </form>
    </FluentGridItem>
</FluentGrid>

@code {
    private const int MaxPasskeyCount = 100;

    private ApplicationUser? user;
    private IList<UserPasskeyInfo>? currentPasskeys;

    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    [SupplyParameterFromForm]
    private string? Action { get; set; }

    [SupplyParameterFromForm]
    private string? CredentialId { get; set; }

    [SupplyParameterFromForm(FormName = "add-passkey")]
    private PasskeyInputModel Input { get; set; } = default!;

    protected override async Task OnInitializedAsync()
    {
        Input ??= new();

        user = await UserManager.GetUserAsync(HttpContext.User);
        if (user is null)
        {
            RedirectManager.RedirectToInvalidUser(UserManager, HttpContext);
            return;
        }
        currentPasskeys = await UserManager.GetPasskeysAsync(user);
    }

    private async Task AddPasskey()
    {
        if (user is null)
        {
            RedirectManager.RedirectToInvalidUser(UserManager, HttpContext);
            return;
        }

        if (!string.IsNullOrEmpty(Input.Error))
        {
            RedirectManager.RedirectToCurrentPageWithStatus($"Error: {Input.Error}", HttpContext);
            return;
        }

        if (string.IsNullOrEmpty(Input.CredentialJson))
        {
            RedirectManager.RedirectToCurrentPageWithStatus("Error: The browser did not provide a passkey.", HttpContext);
            return;
        }

        if (currentPasskeys!.Count >= MaxPasskeyCount)
        {
            RedirectManager.RedirectToCurrentPageWithStatus($"Error: You have reached the maximum number of allowed passkeys.", HttpContext);
            return;
        }

        var attestationResult = await SignInManager.PerformPasskeyAttestationAsync(Input.CredentialJson);
        if (!attestationResult.Succeeded)
        {
            RedirectManager.RedirectToCurrentPageWithStatus($"Error: Could not add the passkey: {attestationResult.Failure.Message}", HttpContext);
            return;
        }

        var addPasskeyResult = await UserManager.AddOrUpdatePasskeyAsync(user, attestationResult.Passkey);
        if (!addPasskeyResult.Succeeded)
        {
            RedirectManager.RedirectToCurrentPageWithStatus("Error: The passkey could not be added to your account.", HttpContext);
            return;
        }

        // Immediately prompt the user to enter a name for the credential
        var credentialIdBase64Url = Base64Url.EncodeToString(attestationResult.Passkey.CredentialId);
        RedirectManager.RedirectTo($"Account/Manage/RenamePasskey/{credentialIdBase64Url}");
    }

    private async Task UpdatePasskey()
    {
        switch (Action)
        {
            case "rename":
                RedirectManager.RedirectTo($"Account/Manage/RenamePasskey/{CredentialId}");
                break;
            case "delete":
                await DeletePasskey();
                break;
            default:
                RedirectManager.RedirectToCurrentPageWithStatus($"Error: Unknown action '{Action}'.", HttpContext);
                break;
        }
    }

    private async Task DeletePasskey()
    {
        if (user is null)
        {
            RedirectManager.RedirectToInvalidUser(UserManager, HttpContext);
            return;
        }

        byte[] credentialId;
        try
        {
            credentialId = Base64Url.DecodeFromChars(CredentialId);
        }
        catch (FormatException)
        {
            RedirectManager.RedirectToCurrentPageWithStatus("Error: The specified passkey ID had an invalid format.", HttpContext);
            return;
        }

        var result = await UserManager.RemovePasskeyAsync(user, credentialId);
        if (!result.Succeeded)
        {
            RedirectManager.RedirectToCurrentPageWithStatus("Error: The passkey could not be deleted.", HttpContext);
            return;
        }

        RedirectManager.RedirectToCurrentPageWithStatus("Passkey deleted successfully.", HttpContext);
    }
}
